package com.tcm.consultation.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.base.core.exception.ServiceException;
import com.base.core.pager.PageBean;
import com.base.core.pager.PageContext;
import com.base.core.utils.GeneratorUtils;
import com.base.core.utils.R;
import com.base.genid.generator.UidGenerator;
import com.base.redis.JedisClient;
import com.base.redis.RedisLock;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tcm.consultation.dao.mapper.ConsultationMapper;
import com.tcm.consultation.dto.ConsultationDTO;
import com.tcm.consultation.dto.PageConsultationDTO;
import com.tcm.consultation.entity.Consultation;
import com.tcm.consultation.enums.BusinessCodeEnum;
import com.tcm.consultation.service.UserConsultationService;
import com.tcm.doctor.domain.dto.DoctorDTO;
import com.tcm.doctor.service.DoctorService;
import com.tcm.ucenter.domain.dto.UserDTO;
import com.tcm.ucenter.service.UserDetailService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

@DubboService(version = "0.0.1-alpha", retries = 3, delay = -1)
public class  UserConsultationServiceImpl implements UserConsultationService {

    @Autowired
    private ConsultationMapper consultationMapper;

    @DubboReference(version = "0.0.1-beta",group = "cached-uid",check = false,retries = 3)
    private UidGenerator uidGenerator;

    @DubboReference(version = "0.0.1-alpha", check = false, retries = 3)
    private DoctorService doctorService;

    @Autowired
    private RedisLock redisLock;

    @Autowired
    private JedisClient jedisClient;

    @DubboReference(version = "0.0.1-alpha", check = false, retries = 3)
    private UserDetailService userDetailService;


    /**
     * 缓存key
     */
    final static String ORDER_INFO = "USER_CONSULTATION_INFO:";

    final static int ORDER_EXPIRE = 900;

    private static final Logger log = LoggerFactory.getLogger(UserConsultationServiceImpl.class);

    /**
     * 获得订单列表
     *
     * @param userId
     * @param start
     * @param length
     * @return
     */
    @Override
    public PageBean<PageConsultationDTO> listPageConsultation(Long userId, Long start, Long length){
        Long count = consultationMapper.countByUserId(userId, null,null);
        PageContext.setCurrentPage(start);
        PageContext.setPageSize(length);
        PageContext.setDataCount(count);
        if(count.intValue() == 0){
            return new PageBean<>(null);
        }

        List<Consultation> consultations = consultationMapper.listPageByUserId(userId,null, null,
                PageContext.getStart(), PageContext.getPageSize());
        Set<Long> userSetIds = consultations.stream().map(consultation -> consultation.getDoctorId()).collect(Collectors.toSet());
        List<Long> userIds = userSetIds.stream().collect(Collectors.toList());
        List<DoctorDTO> doctorDTOS = doctorService.listDoctorByUserIds(userIds);
        //转换主表
        List<PageConsultationDTO> consultationDTOS = consultations.stream().map(consultation -> {
            PageConsultationDTO consultationDTO = new PageConsultationDTO();
            BeanUtils.copyProperties(consultation, consultationDTO);
            return consultationDTO;
        }).collect(Collectors.toList());
        if(doctorDTOS == null || doctorDTOS.isEmpty()){
            //从表为空直接返回主表
            return new PageBean<>(consultationDTOS);
        }
        //从表数据转换为map
        Map<Long, DoctorDTO> longListMap = doctorDTOS.stream().collect(Collectors.toMap(DoctorDTO::getUserId, Function.identity()));
        //主表为准，将从表数据合并主表
        List<PageConsultationDTO> pageConsultationDTOS =  consultationDTOS.stream().map(
                consultation -> {
                    PageConsultationDTO consultationDTO = new PageConsultationDTO();
                    BeanUtils.copyProperties(consultation, consultationDTO);
                    if(longListMap.containsKey(consultation.getDoctorId())){
                        consultationDTO.setDepartment(longListMap.get(consultation.getDoctorId()).getDepartment());
                        consultationDTO.setDoctorAvatar(longListMap.get(consultation.getDoctorId()).getAvatar());
                        consultationDTO.setDoctorName(longListMap.get(consultation.getDoctorId()).getName());
                        consultationDTO.setJobtitle(longListMap.get(consultation.getDoctorId()).getJobtitle());
                    }

                    return consultationDTO;
                }
        ).collect(Collectors.toList());
        return new PageBean<>(pageConsultationDTOS);
    }

    /**
     * 获得订单列表
     *
     * @param userId
     * @param status
     * @param start
     * @param length
     * @return
     */
    @Override
    public PageBean<PageConsultationDTO> listPageConsultation(Long userId, Long patientId, Byte status, Long start, Long length) {
        Long count = consultationMapper.countByUserId(userId, patientId, status);
        PageContext.setCurrentPage(start);
        PageContext.setPageSize(length);
        PageContext.setDataCount(count);
        if(count.intValue() == 0){
            return new PageBean<>(null);
        }

        List<Consultation> consultations = consultationMapper.listPageByUserId(userId,patientId, status,
                PageContext.getStart(), PageContext.getPageSize());
        List<PageConsultationDTO> consultationDTOS =  consultations.stream().map(
                consultation -> {
                    PageConsultationDTO consultationDTO = new PageConsultationDTO();
                    BeanUtils.copyProperties(consultation, consultationDTO);
                    return consultationDTO;
                }
        ).collect(Collectors.toList());
        List<Long> doctorIds = consultations.stream().map(Consultation::getDoctorId).collect(Collectors.toList());
        List<DoctorDTO> doctorDTOS = doctorService.listDoctorByUserIds(doctorIds);
        if(doctorDTOS == null || doctorDTOS.isEmpty()){
            return new PageBean<>(consultationDTOS);
        }
        //转换从表数据为map
        Map<Long, DoctorDTO> longDoctorDTOMap = doctorDTOS.stream().collect(Collectors.toMap(DoctorDTO::getUserId,Function.identity()));
        //主表为准将从表数据合并
        List<PageConsultationDTO> pageConsultationDTOS = consultationDTOS.stream().map(consultation -> {
            if(longDoctorDTOMap.containsKey(consultation.getDoctorId())){
                consultation.setDepartment(longDoctorDTOMap.get(consultation.getDoctorId()).getDepartment());
                consultation.setDoctorAvatar(longDoctorDTOMap.get(consultation.getDoctorId()).getAvatar());
                consultation.setDoctorName(longDoctorDTOMap.get(consultation.getDoctorId()).getName());
                consultation.setJobtitle(longDoctorDTOMap.get(consultation.getDoctorId()).getJobtitle());
            }
            return consultation;
        }).collect(Collectors.toList());
        return new PageBean<>(pageConsultationDTOS);
    }

    /**
     * 获得单个信息
     *
     * @param orderId
     * @return
     */
    @Override
    public ConsultationDTO findConsultationById(Long orderId) throws ServiceException {
        Consultation consultation = consultationMapper.findById(orderId);
        if(ObjectUtil.isNull(consultation)){
            R.throwFail(BusinessCodeEnum.CONSULTATION_ID_QUERY_FAIL);
        }
        DoctorDTO doctorDTO = doctorService.findDoctorByUserId(consultation.getDoctorId());
        ConsultationDTO consultationDTO = new ConsultationDTO();
        BeanUtils.copyProperties(consultation, consultationDTO);
        consultationDTO.setDoctorDTO(doctorDTO);
        return consultationDTO;
    }

    /**
     * 统计订单数
     *
     * @param userId
     * @param status
     * @return
     */
    @Override
    public Long countConsultation(Long userId,Long patientId, Byte status) {
        return consultationMapper.countByUserId(userId,patientId, status);
    }

    /**
     * 从缓存中获取订单
     *
     * @param orderId
     * @return
     */
    @Override
    public ConsultationDTO findFromCache(Long orderId) {
        ConsultationDTO consultationDTO = null;
        try {
            if (redisLock.tryLock(ORDER_INFO + "READLOCK:" + orderId, String.valueOf(orderId))) {
                // 读取缓存
                String json = jedisClient.get(ORDER_INFO + orderId);
                if (json != null) {
                    try {
                        ObjectMapper om = new ObjectMapper();
                        consultationDTO = om.readValue(json, ConsultationDTO.class);
                    } catch (JsonProcessingException e) {
                        R.throwFail(BusinessCodeEnum.ID_QUERY_CACHE_FAIL);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } finally {
            redisLock.unlock(ORDER_INFO + "READLOCK:" + +orderId, String.valueOf(orderId));
        }
        return consultationDTO;
    }

    /**
     * 创建订单
     *
     * @param consultationDTO
     * @return
     */
    @Override
    public Long create(ConsultationDTO consultationDTO) throws ServiceException{
        UserDTO userDTO = userDetailService.findUserByUserId(consultationDTO.getUserId());
        if (userDTO == null) {
            R.throwFail(BusinessCodeEnum.CONSULTATION_USER_NOT_EXISTS);
        }

        // 生成全局唯一订单编号
        Long orderId = uidGenerator.getUID();
        consultationDTO.setId(orderId);


        // 设置订单状态
        // 0待支付，1待接诊，2问诊中，3完成，-1取消
        consultationDTO.setStatus(new Byte("0"));
        consultationDTO.setPaid(new Byte("0"));
        consultationDTO.setOrderSn(GeneratorUtils.genOrderId("01", "1"));
        // 保存订单
        try {
            save(consultationDTO);
            ObjectMapper om = new ObjectMapper();
            jedisClient.set(ORDER_INFO + orderId, om.writeValueAsString(consultationDTO));
            log.info("保存了订单：" + orderId + " 到缓存");
            //重置商品缓存时间
            jedisClient.expire(ORDER_INFO + orderId, ORDER_EXPIRE);
        } catch (JsonProcessingException e) {
            log.error("保存订单：" + orderId + " 到缓存失败");
            R.throwFail(BusinessCodeEnum.CONSULTATION_CTEATE_FAIL);
        }
        return orderId;
    }

    /**
     * 保存订单到数据库
     *
     * @param consultationDTO
     * @return
     */
    @Override
    public int save(ConsultationDTO consultationDTO)  throws ServiceException{
        // 保存订单信息
        Consultation tbOrder = new Consultation();
        BeanUtils.copyProperties(consultationDTO, tbOrder);
        tbOrder.setUpdateTime(DateUtil.date());
        tbOrder.setCreateTime(DateUtil.date());
        if (consultationMapper.insert(tbOrder) != 1) {
            R.throwFail(BusinessCodeEnum.CONSULTATION_CTEATE_FAIL);
        }
        return 1;
    }

    /**
     * 取消订单
     *
     * @param orderId
     * @return
     */
    @Override
    public int cancel(Long orderId) throws ServiceException{
        Consultation consultation = consultationMapper.findById(orderId);
        if(ObjectUtil.isNull(consultation)){
            R.throwFail(BusinessCodeEnum.CONSULTATION_DATA_NOT_EXISTS);
        }
        if(consultation.getPaid().equals(new Byte("1"))
                && consultation.getStatus().compareTo(new Byte("1")) == 1){
            R.throwFail(BusinessCodeEnum.ORDER_PAID);
        }
        consultation = new Consultation();
        consultation.setStatus(new Byte("-1"));
        consultation.setId(orderId);
        consultation.setUpdateTime(DateUtil.date());
        int rs = consultationMapper.update(consultation);
        if(rs != 1){
            R.throwFail(BusinessCodeEnum.UPDATE_FAIL);
        }
        return 1;
    }

    /**
     * 更新支付方式
     *
     * @param orderId
     * @return
     */
    @Override
    public int updatePayment(Long orderId, String payChannel) throws ServiceException{
        Consultation consultation = consultationMapper.findById(orderId);
        if(ObjectUtil.isNull(consultation)){
            R.throwFail(BusinessCodeEnum.CONSULTATION_DATA_NOT_EXISTS);
        }
        if(consultation.getPaid().equals(new Byte("1"))
                && consultation.getStatus().compareTo(new Byte("1")) == 1){
            R.throwFail(BusinessCodeEnum.ORDER_PAID);
        }
        consultation = new Consultation();
        consultation.setId(orderId);
        consultation.setPayChannel(payChannel);
        consultation.setUpdateTime(DateUtil.date());
        int rs = consultationMapper.update(consultation);
        if(rs != 1){
            R.throwFail(BusinessCodeEnum.UPDATE_FAIL);
        }
        return 1;
    }

    /**
     * 订单支付成功
     *
     * @param orderId
     * @return
     */
    @Override
    public int paySuccess(Long orderId)  throws ServiceException{
        Consultation consultation = consultationMapper.findById(orderId);
        if(ObjectUtil.isNull(consultation)){
            R.throwFail(BusinessCodeEnum.CONSULTATION_DATA_NOT_EXISTS);
        }
        if(consultation.getPaid().equals(new Byte("1"))
                && consultation.getStatus().compareTo(new Byte("1")) == 1){
            R.throwFail(BusinessCodeEnum.ORDER_PAID);
        }
        consultation = new Consultation();
        consultation.setStatus(new Byte("1"));
        consultation.setPaid(new Byte("1"));
        consultation.setId(orderId);
        consultation.setUpdateTime(DateUtil.date());
        consultation.setPayTime(DateUtil.date());
        int rs = consultationMapper.update(consultation);
        if(rs != 1){
            R.throwFail(BusinessCodeEnum.UPDATE_FAIL);
        }
        return 1;
    }

    /**
     * 保存缓存到数据库，并删除缓存
     *
     * @param orderId
     * @return
     */
    @Override
    public int saveCacheToDb(Long orderId) throws ServiceException {
        try {
            if (redisLock.tryLock(ORDER_INFO + "READLOCK:" + orderId, String.valueOf(orderId))) {
                // 读取缓存
                String json = jedisClient.get(ORDER_INFO + orderId);
                if (json != null) {
                    try {
                        ObjectMapper om = new ObjectMapper();
                        ConsultationDTO order = om.readValue(json, ConsultationDTO.class);
                        save(order);
                        jedisClient.del(ORDER_INFO + orderId);
                        return 1;
                    } catch (JsonProcessingException e) {
                        R.throwFail(BusinessCodeEnum.ID_QUERY_CACHE_FAIL);
                    } catch (IOException e) {
                        e.printStackTrace();
                        R.throwFail(e.getMessage());
                    }
                }
            }
        } finally {
            redisLock.unlock(ORDER_INFO + "READLOCK:" + +orderId, String.valueOf(orderId));
        }
        return -1;
    }

    /**
     * 删除订单
     *
     * @param orderId
     * @return
     */
    @Override
    public int deleteOrder(Long orderId) {
        return consultationMapper.deleteLogicById(orderId);
    }
}
